package com.huawei.codearts.service;

import com.huawei.codearts.dao.RedisDao;
import com.huawei.codearts.dao.RedisUtilsDao;
import com.huawei.codearts.tools.utils.JsonUtils;
import com.huawei.codearts.tools.utils.RedissonUtils;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class RedisService {

    @Autowired
    private AsyncService asyncService;
    @Autowired
    private RedisDao redisClient;
    private static Logger logger = LoggerFactory.getLogger(RedisService.class);

    /** redis的 set、get、hset、hgetAll、setex等操作 */
    public Object getAndSet() {

        // config配置 方式获取jedis对象
        String key2 = "name222";
        String result2 = redisClient.set(key2, "晓222");
        System.out.println("result2 = " + result2);
        if (Objects.equals(result2, "ok")) {
            System.out.println("result2 = " + result2);
        }
        String data2 = redisClient.get(key2);
        if (!data2.isEmpty()) {
            System.out.println("data2 = " + data2);
        }

        // utils 方式获取jedis对象
        String key = "name";
        String result = RedisUtilsDao.set(key, "晓");
        System.out.println("result = " + result);
        if (Objects.equals(result, "ok")) {
            System.out.println("result = " + result);
        }

        String data = RedisUtilsDao.get(key);
        if (!data.isEmpty()) {
            System.out.println("data = " + data);
        }

        Long s = RedisUtilsDao.del(key);
        if (s == 1) {
            System.out.println("del = " + s);
        }

        Long s2 = RedisUtilsDao.hset("user:1","name","小张");
        if (s2 == 1) {
            System.out.println("hset2 = " + s2);
        }

        Long s3 = RedisUtilsDao.hset("user:1","age","22");
        if (s3 == 1) {
            System.out.println("hset3 = " + s3);
        }

        Map<String, String> s4 = RedisUtilsDao.hgetAll("user:1");
        if (!s4.isEmpty()) {
            System.out.println("hgetAll = " + s4);
            System.out.println("hgetAll = " + s4.get("name"));
        }

        key = "name2";
        result = RedisUtilsDao.setex(key, "晓2", 30);
        System.out.println("name2 result = " + result);
        if (Objects.equals(result, "OK")) {
            System.out.println("name2 result = " + result);
        }

        key = "name3";
        result = RedisUtilsDao.setNxEx(key, "晓3", 30);
        System.out.println("name3 result = " + result);
        if (Objects.equals(result, "OK")) {
            System.out.println("name3 result = " + result);
        }

        key = "name4";
        result = RedisUtilsDao.setNxExLua(key, "晓4", 30);
        System.out.println("name4 result = " + result);
        if (Objects.equals(result, "OK")) {
            System.out.println("name4 result = " + result);
        }

        // 自定义map
        HashMap<String, Object> dataMap = new HashMap<>();
//        dataMap.put("id", member.getId());
//        dataMap.put("user", member.getUser());

        return dataMap;
    }

    /** redis的 lock、unlock等操作 */
    public Object lock() throws InterruptedException {

        // 自定义map
        HashMap<String, Object> dataMap = new HashMap<>();

        /** php 执行 lua redis脚本 */
//        <?php
//        $redis = new Redis();
//        $redis->connect('127.0.0.1', 6379);
//        // Lua脚本来增加键的值，并返回新的值
//        $luaScript = <<<LUA
//        local val = redis.call('incr', KEYS[1])
//        return val
//        LUA;
//        // 需要执行的Redis命令的参数
//        $params = ['counter_key'];
//        // 执行Lua脚本
//        $result = $redis->eval($luaScript, $params);
//        echo "Counter value after increment: " . $result;

        /*
         * redis 分布式锁 + lua删除锁  未完成部分 >> 操作共享资源：没操作完之前，开启守护线程，定期给锁续期
         * 所有分布式锁 都不能100%安全，所以分布式锁+数据库的乐观锁，2种结合使用。
         *
         * 1、使用分布式锁，在上层完成「互斥」目的，虽然极端情况下锁会失效，但它可以最大程度把并发请求阻挡在最上层，减轻操作资源层的压力。
         * 2、但对于要求数据绝对正确的业务，在资源层一定要做好「兜底」，设计思路可以借鉴 fecing token 的方案来做，即在 DB 层通过版本号的方式来更新数据，避免并发冲突。
         */
        String uuid = RedisService.getRequestId();
        if (RedisUtilsDao.lock("lock", uuid, 30)) {
            System.out.println("获取到锁了, uuid:" + uuid);
            Thread.sleep(2000);
            // 删除锁
            RedisUtilsDao.unlock("lock", uuid);
        } else {
            System.out.println("未获取到锁");
        }

        /*
         * Redisson看门狗 锁
         */
        RedissonClient redisson = RedissonUtils.getRedisson();
        RLock lock = redisson.getLock("redLock");
        boolean isLock = lock.tryLock(1L, TimeUnit.SECONDS); //锁的过期时间默认为30s
        if (!isLock) {
//            log.error("获取锁失败 .... 1");
            System.out.println("获取锁失败 .... 1");
            return dataMap;
        }
        try {
//            log.info("获取锁成功 .... 1");
//            method2();
//            log.info("开始执行业务 ... 1");
            System.out.println("获取锁成功 .... 1");
            System.out.println("开始执行业务 .... 1");
            Thread.sleep(2000);

        } finally {
//            log.warn("准备释放锁 .... 1");
            System.out.println("准备释放锁 .... 1");
            lock.unlock();
        }

        // 日志测试
        // https://blog.csdn.net/m0_54144956/article/details/125971269
        logger.info("log 日志测试!!!");
        // lombok 日志
        log.info("记录日志使用 lombok 插件提供注解@Slf4j");


//        // 脚本函数可以存在字符串中，函数名为ntc
//        String formula = "function ntc(n) return 2*n end";
//        // 传入脚本函数的参数
//        double ptc = 10.1;
//        // Lua环境
//        Globals globals = JsePlatform.standardGlobals();
//        // 加载lua脚本内容
//        LuaValue chunk = globals.load(formula);
//        // 执行
//        chunk.call();
//        // 获取Lua函数，函数名为ntc
//        LuaValue func = globals.get("ntc");
//        // 调用函数，传入参数
//        LuaValue computeTemp = func.call(LuaValue.valueOf(ptc));
//        // 结果转换
//        double computedTemp = computeTemp.checkdouble();
////        log.info("计算出的温度值=={}, computedTemp);
//        System.out.println("计算出的温度值==" + computedTemp);



//        dataMap.put("id", member.getId());
//        dataMap.put("user", member.getUser());

        return dataMap;
    }

    // uuid
    private static String getRequestId() {
        return UUID.randomUUID().toString();
    }

    // 有序集合 延迟队列demo
    public long zadd() throws IOException {
        String key = "task::queue::defer";
        Map<String, Object> mapData = new ConcurrentHashMap<>();
        mapData.put("messageId", RedisService.getRequestId());
        mapData.put("data", Map.of("id", 1L, "name", "root"));
        String json = JsonUtils.mapToJson(mapData);
        Double score = (double) Instant.now().getEpochSecond() + 60;  // 延迟1分钟在进行处理
        long success = redisClient.zadd(key, score, json);
        System.out.println("RedisService zadd success = " + success);

        // 测试
//        long maxScore = (long) score.doubleValue();
//        long number = redisClient.zsetTransferList(key, 0L, maxScore, "task::queue");
//        System.out.println("redis延迟队列等待: number=" + number + ", thread=" + Thread.currentThread().getName());

        return success;
    }

    // 队列 入队demo
    public long push() throws IOException {
        String key = "task::queue";
        Map<String, Object> mapData = new ConcurrentHashMap<>();
        mapData.put("messageId", RedisService.getRequestId());
        mapData.put("data", Map.of("id", 1L, "name", "root"));
        String json = JsonUtils.mapToJson(mapData);
        long success = redisClient.lpush(key, json);
        System.out.println("RedisService push success = " + success);
        return success;
    }

    public Map<String, Object> queue() throws IOException, InterruptedException {

//        asyncService.queueTask();  // 模拟队列任务

        Map<String, Object> dataMap = new HashMap<>();
        String key = "task::queue";
        String json = redisClient.rpop(key);
        if (!json.isEmpty()) {
            System.out.println("json = " + json);
            dataMap = JsonUtils.jsonToMap(json);
        }
        return dataMap;
    }

}
